﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;

namespace LambdaToSql.FrameWork
{
    /// <summary>
    /// 工具类
    /// </summary>
    public class Tools
    {
        #region 属性设置 取值 

        /// <summary>
        /// 实体获取属性值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="Name"></param>
        /// <returns></returns>
        internal static object GetProperty<T>(T source, string Name) where T : class, new()
        {
            PropertyInfo[] propertys = source.GetType().GetProperties();// 获得此模型的公共属性
            PropertyInfo pi = propertys.Where(ex => ex.Name == Name).FirstOrDefault();
            if (pi != null)
            {
                return pi.GetValue(source, null);
            }
            return null;
        }

        /// <summary>
        /// 实体设置赋值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="Name"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        internal static bool SetProperty<T>(T source, string Name, object value) where T : class, new()
        {
            try
            {
                PropertyInfo[] propertys = source.GetType().GetProperties();// 获得此模型的公共属性
                PropertyInfo pi = propertys.Where(ex => ex.Name == Name).FirstOrDefault();
                if (pi != null)
                {
                    pi.SetValue(source, value, null);
                }
                return true;
            }
            catch
            {
                return false;
            }
        }
        #endregion

        #region 表达式类型转换

        /// <summary>
        /// 表达式类型转换
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        internal static string TypeCast(ExpressionType type)
        {
            switch (type)
            {
                case ExpressionType.And:
                case ExpressionType.AndAlso:
                    return " and ";
                case ExpressionType.Equal:
                    return " = ";
                case ExpressionType.GreaterThan:
                    return " > ";
                case ExpressionType.GreaterThanOrEqual:
                    return " >= ";
                case ExpressionType.LessThan:
                    return " < ";
                case ExpressionType.LessThanOrEqual:
                    return " <= ";
                case ExpressionType.NotEqual:
                    return " <> ";
                case ExpressionType.Or:
                case ExpressionType.OrElse:
                    return " or ";
                case ExpressionType.Add:
                case ExpressionType.AddChecked:
                    return " + ";
                case ExpressionType.Subtract:
                case ExpressionType.SubtractChecked:
                    return " - ";
                case ExpressionType.Divide:
                    return " / ";
                case ExpressionType.Multiply:
                case ExpressionType.MultiplyChecked:
                    return " * ";
                default:
                    return null;
            }
        }
        #endregion

        #region GetType 

        /// <summary>
        /// 将SQLServer数据类型(如:varchar)转换为.Net类型(如:String) 
        /// </summary>
        /// <param name="sqlType"></param>
        /// <returns></returns>
        internal static string GetType(string sqlType)
        {
            try
            {
                string[] SqlTypeNames = new string[] { "int", "varchar","bit" ,"datetime","decimal","float","image","money",
                                                    "ntext","nvarchar","smalldatetime","smallint","text","bigint","binary","char","nchar","numeric",
                                                    "real","smallmoney", "sql_variant","timestamp","tinyint","uniqueidentifier","varbinary"};

                string[] CSharpTypes = new string[] {"Nullable<int>", "string","Nullable<bool>" ,"Nullable<DateTime>","Nullable<Decimal>","Nullable<Double>","Byte[]","Nullable<Single>",
                                                "string","string","Nullable<DateTime>","Nullable<Int16>","string","Nullable<Int64>","Byte[]","string","string","Nullable<Decimal>",
                                                "Nullable<Single>","Nullable<Single>", "Object","Byte[]","Nullable<Byte>","Nullable<Guid>","Byte[]"};

                int i = Array.IndexOf(SqlTypeNames, sqlType.ToLower());
                return CSharpTypes[i];
            }
            catch (Exception ex)
            {
                return "string";
            }
        }
        #endregion

        #region 文件读写

        /// <summary>
        /// 文件写入方法
        /// </summary>
        /// <param name="path">文件路径:绝对路径</param>
        /// <param name="str">写入内容</param>
        /// <param name="append">确定是否将数据追加到文件。如果该文件存在，并且 append 为 false，则该文件被覆盖。如果该文件存在，并且 append 为 true，则数据被追加到该文件中。否则，将创建新文件。</param>
        /// <param name="encoding">编码格式</param>
        internal static void FileWrite(string path, string str, bool append = false, string encoding = "utf-8")
        {
            System.Text.Encoding code = System.Text.Encoding.GetEncoding(encoding);
            System.IO.StreamWriter sw = null;
            try
            {
                sw = new System.IO.StreamWriter(path, append, code);
                sw.Write(str);
                sw.Flush();
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                sw.Close();
                sw.Dispose();
            }
        }

        /// <summary>
        /// 文件读取方法
        /// </summary>
        /// <param name="path">文件路径:绝对路径</param>
        /// <param name="encoding">编码格式</param>
        internal static string FileRead(string path, string encoding = "utf-8")
        {
            //源码是替换掉模板中的特征字符            
            //string Path = System.Web.HttpContext.Current.Server.MapPath(path);
            Encoding code = Encoding.GetEncoding(encoding);
            System.IO.StreamReader Sr = null;

            string str = "";

            //读取
            try
            {
                Sr = new StreamReader(path, code);
                str = Sr.ReadToEnd();
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                Sr.Close();
            }
            return str;
        }

        #endregion 文件读写

        #region 实体赋值

        /// <summary>
        /// 设置赋值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="K"></typeparam>
        /// <param name="source">源数据对象</param>
        /// <param name="target">赋值目标对象</param>
        public static void SetBase<T, K>(T source, K target) where T : class, new()
        {
            if (source != null && target != null)
            {
                PropertyInfo[] propertys1 = source.GetType().GetProperties();// 获得此模型的公共属性
                PropertyInfo[] propertys = target.GetType().GetProperties();
                foreach (PropertyInfo pi in propertys)
                {
                    if (pi.CanWrite && propertys1.Where(ex => ex.Name == pi.Name).Count() > 0)
                    {
                        object value = GetProperty(source, pi.Name);
                        pi.SetValue(target, value, null);
                    }
                }
            }
        }

        /// <summary>
        /// 设置赋值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="K"></typeparam>
        /// <param name="source">源数据对象</param>
        /// <param name="target">赋值目标对象</param>
        public static void SetBase<T, K>(List<T> source, List<K> target) where T : class, new() where K : class, new()
        {
            if (source != null && target != null)
            {
                foreach (var item in source)
                {
                    var obj = new K();
                    SetBase(item, obj);
                    target.Add(obj);
                }
            }
        }

        /// <summary>
        /// 更新赋值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="K"></typeparam>
        /// <param name="source">源数据对象</param>
        /// <param name="target">赋值目标对象</param>
        public static void SetUpdateBase<T, K>(T source, K target) where T : class, new()
        {
            PropertyInfo[] propertys1 = source.GetType().GetProperties();// 获得此模型的公共属性
            PropertyInfo[] propertys = target.GetType().GetProperties();
            foreach (PropertyInfo pi in propertys)
            {
                if (pi.CanWrite && propertys1.Where(ex => ex.Name == pi.Name).Count() > 0 && pi.Name.ToUpper() != "GUID")
                {
                    object value = GetProperty(source, pi.Name);
                    if (value != null)
                    {
                        pi.SetValue(target, value, null);
                    }
                }
            }
        }
        #endregion        

        #region DataTable转IList

        /// <summary>
        /// DataTable转IList集合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="Dt"></param>
        /// <param name="propertys">T GetProperties</param>
        /// <returns></returns>
        internal static IList<T> DataTableToList<T>(DataTable Dt, PropertyInfo[] propertys = null) where T : class, new()
        {
            IList<T> list = new List<T>();// 定义集合
            if (Dt != null)
            {
                if (propertys == null)
                {
                    //propertys = typeof(T).GetType().GetProperties();// 获得此模型的公共属性
                    propertys = typeof(T).GetProperties();// 获得此模型的公共属性
                }
                foreach (DataRow dr in Dt.Rows)
                {
                    T t = new T();
                    foreach (PropertyInfo pi in propertys)
                    {
                        var name = pi.Name;
                        if (Dt.Columns.Contains(name))
                        {
                            if (pi.CanWrite)
                            {
                                object value = dr[name];
                                if (pi.PropertyType == typeof(string))
                                {
                                    pi.SetValue(t, value.ToString(), null);
                                }
                                else if (pi.PropertyType == typeof(int) || pi.PropertyType == typeof(int?))
                                {
                                    pi.SetValue(t, int.Parse(value.ToString()), null);
                                }
                                else if (pi.PropertyType == typeof(DateTime?) || pi.PropertyType == typeof(DateTime))
                                {
                                    pi.SetValue(t, DateTime.Parse(value.ToString()), null);
                                }
                                else if (pi.PropertyType == typeof(float))
                                {
                                    pi.SetValue(t, float.Parse(value.ToString()), null);
                                }
                                else if (pi.PropertyType == typeof(double))
                                {
                                    pi.SetValue(t, double.Parse(value.ToString()), null);
                                }
                                else
                                {
                                    pi.SetValue(t, value, null);
                                }
                            }
                        }
                    }
                    list.Add(t);
                }
            }
            return list;
        }


        #endregion

        #region IDataReader to IList<T>

        /// <summary>
        /// IDataReader转IList
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="idr"></param>
        /// <returns></returns>
        internal static IList<T> IDataReaderToIlist<T>(IDataReader idr)
        {
            var list = new List<T>();
            var builder = IDataReaderEntityBuilder<T>.CreateBuilder(idr);
            while (idr.Read())
            {
                var entity = builder.Build(idr);
                list.Add(entity);
            }
            idr.Close();

            return list;
        }

        /// <summary>
        /// IDataReader转 T
        /// <para>取第一条数据</para>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="idr"></param>
        /// <returns></returns>
        public static T IDataReaderToEntity<T>(IDataReader idr) where T : class, new()
        {
            var entity = new T();
            var builder = IDataReaderEntityBuilder<T>.CreateBuilder(idr);
            if (idr.Read())
            {
                entity = builder.Build(idr);
            }
            idr.Close();

            return entity;
        }


        ///// <summary>
        ///// IDataReader转IList
        ///// </summary>
        ///// <typeparam name="T"></typeparam>
        ///// <param name="idr"></param>
        ///// <returns></returns>
        //internal static IList<T> IDataReaderToIlist<T>(IDataReader idr)
        //{
        //    var list = new List<T>();
        //    var builder = IDataReaderEntityBuilder<T>.CreateBuilder(idr);
        //    while (idr.Read())
        //    {
        //        var entity = builder.Build(idr);
        //        list.Add(entity);
        //    }
        //    idr.Close();

        //    return list;
        //}

        ///// <summary>
        ///// IDataReader转 T
        ///// <para>取第一条数据</para>
        ///// </summary>
        ///// <typeparam name="T"></typeparam>
        ///// <param name="idr"></param>
        ///// <returns></returns>
        //public static T IDataReaderToEntity<T>(IDataReader idr) where T : class, new()
        //{
        //    var entity = new T();
        //    var builder = IDataReaderEntityBuilder<T>.CreateBuilder(idr);
        //    if (idr.Read())
        //    {
        //        entity = builder.Build(idr);
        //    }
        //    idr.Close();

        //    return entity;
        //}

        private class IDataReaderEntityBuilder<Entity>
        {
            private static readonly MethodInfo getValueMethod = typeof(IDataRecord).GetMethod("get_Item", new Type[] { typeof(int) });
            private static readonly MethodInfo isDBNullMethod = typeof(IDataRecord).GetMethod("IsDBNull", new Type[] { typeof(int) });
            private delegate Entity Load(IDataRecord dataRecord);

            private Load handler;
            private IDataReaderEntityBuilder() { }

            public Entity Build(IDataRecord dataRecord)
            {
                return handler(dataRecord);
            }

            public static IDataReaderEntityBuilder<Entity> CreateBuilder(IDataRecord idr)
            {
                IDataReaderEntityBuilder<Entity> dynamicBuilder = new IDataReaderEntityBuilder<Entity>();
                DynamicMethod method = new DynamicMethod("IDataReaderDynamicCreateEntity", typeof(Entity), new Type[] { typeof(IDataRecord) }, typeof(Entity), true);
                ILGenerator generator = method.GetILGenerator();
                LocalBuilder result = generator.DeclareLocal(typeof(Entity));
                generator.Emit(OpCodes.Newobj, typeof(Entity).GetConstructor(Type.EmptyTypes));
                generator.Emit(OpCodes.Stloc, result);

                var properties = typeof(Entity).GetProperties();
                for (int i = 0; i < idr.FieldCount; i++)
                {
                    //PropertyInfo propertyInfo = typeof(Entity).GetProperty(properties.FirstOrDefault(x => x.Name.ToUpper().Equals(dataRecord.GetName(i)))?.Name);
                    PropertyInfo propertyInfo = typeof(Entity).GetProperty(idr.GetName(i));
                    Label endIfLabel = generator.DefineLabel();
                    if (propertyInfo != null && propertyInfo.GetSetMethod() != null)
                    {
                        generator.Emit(OpCodes.Ldarg_0);
                        generator.Emit(OpCodes.Ldc_I4, i);
                        generator.Emit(OpCodes.Callvirt, isDBNullMethod);
                        generator.Emit(OpCodes.Brtrue, endIfLabel);
                        generator.Emit(OpCodes.Ldloc, result);
                        generator.Emit(OpCodes.Ldarg_0);
                        generator.Emit(OpCodes.Ldc_I4, i);
                        generator.Emit(OpCodes.Callvirt, getValueMethod);
                        generator.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType);
                        generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod());
                        generator.MarkLabel(endIfLabel);
                    }
                }
                generator.Emit(OpCodes.Ldloc, result);
                generator.Emit(OpCodes.Ret);
                dynamicBuilder.handler = (Load)method.CreateDelegate(typeof(Load));
                return dynamicBuilder;
            }
        }


        #endregion

        #region 动态加载程序集

        /// <summary>
        /// 动态加载程序集,参数类型是泛型 [T]
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="assemblyName">程序集名称</param>
        /// <param name="fullClassName">全名称 例如: LambdaToSql.Realization.MsSqlServer.Queryable`1</param>
        /// <param name="par">参数</param>
        /// <returns></returns>
        internal static object GetReflectionObj<T>(string assemblyName, string fullClassName, object par = null) where T : class, new()
        {
            if (par != null)
            {
                Type objType = Assembly.Load(assemblyName).GetType(fullClassName);
                Type type = objType.MakeGenericType(typeof(T));
                var result = Activator.CreateInstance(type, new object[] { par });
                return result;
            }
            else
            {
                var result = Assembly.Load(assemblyName).CreateInstance(fullClassName);
                return result;
            }
        }

        /// <summary>
        /// 动态加载程序集
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="assemblyName">程序集名称</param>
        /// <param name="fullClassName">全名称 例如: LambdaToSql.Realization.MsSqlServer.Queryable`1</param>
        /// <param name="par">参数</param>
        /// <returns></returns>
        internal static object GetReflectionObj(string assemblyName, string fullClassName, object par = null)
        {
            if (par != null)
            {
                Type objType = Assembly.Load(assemblyName).GetType(fullClassName);            
                object result = Activator.CreateInstance(objType, new object[] { par });//创建实例
                return result;
            }
            else
            {
                var result = Assembly.Load(assemblyName).CreateInstance(fullClassName);
                return result;
            }
        }

        #endregion

    }
}
